# ifndef GRID_H
# define GRID_H

# include <tpl_euclidian_graph.H>
# include <package.H>
# include <configuration.H>

class Grid;

class Grid_Node;

class Package;

class Op_Node;

class Op_Arc;

struct Deliveried_List{
  DynDlist<Package *> list;
  size_t counter;
  double percent;
  Deliveried_List(): counter(0), percent(0.0){}
};


/**
  * \brief Modela un nodo de la Malla.
  *
  * La clase se basa en Euclidian_Node<Node_Info> de la biblioteca
  * <a url="http://webdelprofesor.ula.ve/ingenieria/lrleon/aleph/html/index.html" target="_blank">ALEPH-w</a>
  * con par&aacute;metro tipo long.
  * @authors Alejandro Mujica, Anna Lezama
  */
class Grid_Node : public Euclidian_Node<long>
{
  bool __is_in;
  bool __is_out;

  DynMapTreap < Grid_Node * , Deliveried_List> deliveried_packages;

  DynDlist<Package *> clients;
  DynDlist<Package *> queue;

  unsigned long num_nodes_deflected;
  unsigned long num_nodes_routed;

  size_t unsorted_counter;

  double percent_total;

  unsigned long packages_deliveried_counter;

  unsigned long packages_generated_counter;

public:
  Grid_Node();

  Grid_Node(const long &);

  Grid_Node(Grid_Node *);

  Grid_Node(const Point &);

  Grid_Node(const long &, const Point &);

  bool & is_in();

  bool & is_out();

  const unsigned long & get_num_nodes_deflected() const;

  const unsigned long & get_num_nodes_routed() const;

  const double & get_percent_total() const;

  const unsigned long & get_packages_deliveried() const { return packages_deliveried_counter;}

  const unsigned long & get_packages_generated() const { return packages_generated_counter;}

  void set_percent_total(double &);

  void inc_num_nodes_deflected();

  void inc_num_nodes_routed();

  void inc_packages_generated();

  const bool is_io() const;

  DynMapTreap < Grid_Node * , Deliveried_List> & get_deliveried_packages(){ return deliveried_packages;}

  /**
    * Coloca un paquete en la lista de clientes del nodo para ser enrutado posteriormente.
    */
  void add_client(Package *);

  /**
    * Coloca un paquete en la cola de espera del nodo, esto ocurre &uacute;nicamente cuando el paquete
    * intenta entrar en la malla pero el nodo por el que llega tiene su lista de clientes llena.
    */
  void put_in_queue(Package *);

  DynDlist<Package *> & get_clients_list();

  DynDlist<Package *> & get_queue();

  void add_deliveried(Package *);

  /**
    * Retorna una descripci&oacute;n del nodo, en este caso es la palabra Nodo con el n&uacute;mero que lo identifica.
    */
  const std::string to_string() const;

  void reset_node();
};

/**
  * \brief Modela un enlace de la Malla.
  *
  * La clase se basa en Euclidian_Arc<Arc_Info> de la biblioteca
  * <a url="http://webdelprofesor.ula.ve/ingenieria/lrleon/aleph/html/index.html" target="_blank">ALEPH-w</a>
  * sin peso.
  * Euclidian_Arc modela un arco de grafo con nodo fuente y nodo destino y se puede representar de la siguiente
  * manera: src_node -- tgt_node.
  * En un grafo no dirigido, src_node y tgt_node no tienen diferencia ya que cualquiera de los dos nodos pueden ser
  * fuente o destino en cualquier momento.
  * @authors Alejandro Mujica, Anna Lezama
  */
class Link : public Euclidian_Arc<Empty_Class>
{
  Configuration * conf;
  Direction dir;

  int capacity;

  DynDlist<Package * > packages_from_src;
  DynDlist<Package *> packages_from_tgt;

public:
  Link();

  Link(const Empty_Class &);

  Link(void *, void *, const Empty_Class &);

  Link(void *, void *);


  /**
    * Retorna la direcci&oacute;n (Norte, Sur, Este, Oeste) del arco respecto al nodo fuente (modificable).
    */
  Direction & get_direction();

  /**
    * Retorna la direcci&oacute;n (Norte, Sur, Este, Oeste) del arco respecto al nodo fuente (constante).
    */
  const Direction & get_direction() const;

  /**
    * Retorna el paquete que est&aacute; pasando por el arco que viene del nodo fuente (src_node).
    */
  Package * get_first_package_from_src();

  void add_package_from_src(Package *);

  /**
    * Paquete que est&aacute; pasando por el arco que viene del nodo destino (tgt_node).
    */
  Package * get_first_package_from_tgt();

  void add_package_from_tgt(Package *);

  const bool is_busy_for_src() const;

  const bool is_busy_for_tgt() const;

  void reset();

  Link & operator = (const Link &);

};


/**
  * \brief Modela una Malla 2D.
  *
  * La clase est&aacute; basada en Euclidian_Graph<__Euclidian_Node, __Euclidian_Arc> de la biblioteca
  * <a url="http://webdelprofesor.ula.ve/ingenieria/lrleon/aleph/html/index.html" target="_blank">ALEPH-w</a>
  * con par&aacute;metros tipo Grid_Node y Link.
  */
class Grid : public Euclidian_Graph<Grid_Node, Link>
{
  size_t width;
  size_t height;
  
public:
  typedef Euclidian_Graph<Grid_Node, Link> Graph;

  Grid();

  Grid(const size_t &, const size_t &);

  Grid(const Grid &);

  ~Grid();

  void set_height(const size_t &);

  const size_t & get_height() const;

  void set_width(const size_t &);

  const size_t & get_width() const;

  void build_automatic_grid();

  Node * insert_grid_node(const int &, const long &, const long &);

  Arc * insert_arc(Node *, Node *);

  /**
    * Retorna la direcci&oacute;n de un enlace respecto a un nodo.
    * @param arc Arco al que se le quiere conocer la direcci&oacute;n.
    * @param node Nodo respecto al cual se desea conocer la direcci&oacute;n del arco.
    */
  Direction get_arc_direction_respect_node(Arc *, Node *);

  /**
    * Retorna el enlace que, euclidianamente, se encuentra a una direcci&oacute;n dada del nodo deseado.
    * @param node Nodo desde donde se desea el enlace
    * @param dir La direcci&oacute;n a la que se quiere acceder.
    */
  Arc * get_arc_by_rout(Node *, const Direction &);

  const bool is_arc_busy_for_node(Arc *, Node *);

  /**
    * Retorna el primer enlace adyacente a un nodo que no est&eacute; ocupado.
    */
  Arc * get_first_free_link(Node *);

  void set_arc_busy_for_node(Arc *, Node *, Package *);

  bool is_arc_horizontal(Arc *);

  bool is_arc_vertical(Arc *);

  void reset_all_arcs();

  void clear();
};

/**
  * \brief Operaci&oacute;n que se realiza sobre cada nodo el crear la Malla.
  *
  * Esta esla clase que se le pasa como par&aacute;metro a la clase Grid_Builder de operaci&oacute;n
  * a realizar sobre el nodo cuando lo crea, lo que hace es asignarle las coordenadas euclidianas.
  */
class Op_Node
{
public:
  void operator () (Grid & g, Grid_Node * node, const size_t & i, const size_t & j)
  {
    node->get_info() = g.get_width() * i + j;
    node->get_position() = Point(j, i);
  }
};

# endif
